/*
 * DocumentHandler2Item.java
 *
 * Created on June 12, 2005, 11:21 AM
 *
 * (C) R. Alexander Milowski alex@milowski.com
 */

package org.infoset.xml.xerces;

import java.util.*;
import java.net.*;

import org.infoset.xml.*;

import org.apache.xerces.xni.*;
import org.apache.xerces.xs.*;
import org.apache.xerces.impl.Constants;

/**
 *
 * @author R. Alexander Milowski
 */
public class DocumentHandler2Item implements XMLDocumentHandler
{
   
   static final int INCREMENT = 20;

   Infoset infoset;
   ItemConstructor constructor;
   ItemDestination output;
   NamespaceContext namespaceContext;
   Name [] names;
   TypeDefinition [] types;
   int level;
   
   /** Creates a new instance of DocumentHandler2Item */
   public DocumentHandler2Item()
   {
      this(InfosetFactory.getDefaultInfoset(), null);
   }
   
   /** Creates a new instance of DocumentHandler2Item */
   public DocumentHandler2Item(ItemDestination dest)
   {
      this(InfosetFactory.getDefaultInfoset(), dest);
   }
   
   /** Creates a new instance of DocumentHandler2Item */
   public DocumentHandler2Item(Infoset infoset,ItemDestination dest)
   {
      this.infoset = infoset;
      this.constructor = infoset.createItemConstructor();
      this.output = dest;
      this.names = new Name[INCREMENT];
      this.types = new TypeDefinition[INCREMENT];
      this.level = -1;
   }
   
   public void attach(ItemDestination dest) {
      output = dest;
   }

   public void characters(XMLString xMLString, Augmentations augmentations) throws XNIException
   {
      try {
         output.send(constructor.createCharacters(xMLString.toString()));
      } catch (XMLException ex) {
         throw new XNIException(ex.getMessage(),ex);
      }
   }

   public void comment(XMLString xMLString, Augmentations augmentations) throws XNIException
   {
      try {
         output.send(constructor.createComment(xMLString.toString()));
      } catch (XMLException ex) {
         throw new XNIException(ex.getMessage(),ex);
      }
   }

   public void doctypeDecl(String str, String str1, String str2, Augmentations augmentations) throws XNIException
   {
   }
   
   protected Element createElement(QName qName, XMLAttributes atts, Augmentations augmentations)
      throws URISyntaxException,XMLException
   {
      Name nm = infoset.createName(
                    qName.uri==null ? Name.NO_NAMESPACE : infoset.createNamespace(qName.uri),
                    qName.localpart);
      Element e = constructor.createElement(nm);
      e.setPrefix(qName.prefix);
      
      // Add all attributes
      int attCount = atts.getLength();
      for (int i=0; i<attCount; i++) {
         String localName = atts.getLocalName(i);
         String prefix = atts.getPrefix(i);
         String uri = atts.getURI(i);
         String value = atts.getValue(i);
         Augmentations attAugs = atts.getAugmentations(i);
         URI ns = uri==null ? Name.NO_NAMESPACE : infoset.createNamespace(uri);
         Name attnm = infoset.createName(ns,localName);
         Attribute att = e.setAttributeValue(attnm,value);
         AttributePSVI attPSVI = (AttributePSVI)attAugs.getItem(Constants.ATTRIBUTE_PSVI);
         if (attPSVI!=null) {
            XSTypeDefinition attTDef = attPSVI.getTypeDefinition();
            if (attTDef!=null) {
               att.setTypeDefinition(new XercesTypeDefinition(attTDef));
            }
            int validity = Validity.NOT_KNOWN;
            int attempted = Validity.ATTEMPTED_NONE;
            switch (attPSVI.getValidationAttempted()) {
               case ItemPSVI.VALIDATION_NONE:
                  attempted = Validity.ATTEMPTED_NONE;
                  break;
               case ItemPSVI.VALIDATION_PARTIAL:
                  attempted = Validity.ATTEMPTED_PARTIAL;
                  break;
               case ItemPSVI.VALIDATION_FULL:
                  attempted = Validity.ATTEMPTED_FULL;
            }
            switch (attPSVI.getValidity()) {
               case ItemPSVI.VALIDITY_INVALID:
                  validity = Validity.INVALID;
                  break;
               case ItemPSVI.VALIDITY_VALID:
                  validity = Validity.VALID;
                  break;
               case ItemPSVI.VALIDITY_NOTKNOWN:
                  validity = Validity.NOT_KNOWN;
            }
            att.setValidity(validity, attempted);
            StringList slist = attPSVI.getErrorCodes();
            if (slist!=null) {
               int elen = slist.getLength();
               for (int j=0; j<elen; j++) {
                  att.addError(slist.item(j));
               }
            }
         }
         
      }
      
      // Add all namespace declarations
      int nsCount = namespaceContext.getDeclaredPrefixCount();
      for (int i=0; i<nsCount; i++) {
         String prefix = namespaceContext.getDeclaredPrefixAt(i);
         String uri = namespaceContext.getURI(prefix);
         URI ns = infoset.createNamespace(uri);
         //System.out.println("declaring prefix: "+prefix+" -> "+ns);
         e.addNamespaceBinding(prefix,ns);
      }
      
      // Handle PSVI:
      ElementPSVI psvi = (ElementPSVI)augmentations.getItem(Constants.ELEMENT_PSVI);
      if (psvi!=null) {
         XSTypeDefinition tdef = psvi.getTypeDefinition();
         StringList slist = psvi.getErrorCodes();
         if (slist!=null) {
            int elen = slist.getLength();
            for (int i=0; i<elen; i++) {
               e.addError(slist.item(i));
            }
         }
         if (tdef!=null) {
            e.setTypeDefinition(new XercesTypeDefinition(tdef));
         //} else {
         //   System.out.println("Type null on start for "+nm);
         }
      }
      
      return e;
   }

   protected ElementEnd createElementEnd(Name nm, TypeDefinition tdef,Augmentations augmentations)
      throws XMLException
   {
      ElementEnd e = constructor.createElementEnd(nm);

      // Handle PSVI:
      ElementPSVI psvi = (ElementPSVI)augmentations.getItem(Constants.ELEMENT_PSVI);
      if (psvi!=null) {
         XSTypeDefinition xstdef = psvi.getTypeDefinition();
         //if (xstdef==null) {
         //   System.out.println("Type null on end for "+nm);
         //}
         e.setTypeDefinition(tdef);
         int validity = Validity.NOT_KNOWN;
         int attempted = Validity.ATTEMPTED_NONE;
         switch (psvi.getValidationAttempted()) {
            case ItemPSVI.VALIDATION_NONE:
               attempted = Validity.ATTEMPTED_NONE;
               break;
            case ItemPSVI.VALIDATION_PARTIAL:
               attempted = Validity.ATTEMPTED_PARTIAL;
               break;
            case ItemPSVI.VALIDATION_FULL:
               attempted = Validity.ATTEMPTED_FULL;
         }
         switch (psvi.getValidity()) {
            case ItemPSVI.VALIDITY_INVALID:
               validity = Validity.INVALID;
               break;
            case ItemPSVI.VALIDITY_VALID:
               validity = Validity.VALID;
               break;
            case ItemPSVI.VALIDITY_NOTKNOWN:
               validity = Validity.NOT_KNOWN;
         }
         e.setValidity(validity, attempted);
         StringList slist = psvi.getErrorCodes();
         if (slist!=null) {
            int elen = slist.getLength();
            for (int i=0; i<elen; i++) {
               e.addError(slist.item(i));
            }
         }
      }
      
      return e;
   }

   public void emptyElement(QName qName, XMLAttributes xMLAttributes, Augmentations augmentations) throws XNIException
   {
      try {
         Element start = createElement(qName,xMLAttributes,augmentations);
         Name nm = start.getName();
         //System.out.println("Empty element: "+nm+", "+level);
         ElementEnd end = createElementEnd(nm,start.getTypeDefinition(),augmentations);
         start.setValidity(end.getValidity(),end.getValidationAttempted());
         output.send(start);
         output.send(end);
      } catch (XMLException ex) {
         throw new XNIException(ex.getMessage(),ex);
      } catch (URISyntaxException ex) {
         throw new XNIException(ex.getMessage(),ex);
      }
   }

   public void endCDATA(Augmentations augmentations) throws XNIException
   {
   }

   public void endDocument(Augmentations augmentations) throws XNIException
   {
      try {
         output.send(constructor.createDocumentEnd());
      } catch (XMLException ex) {
         throw new XNIException(ex.getMessage(),ex);
      }
   }

   public void endElement(QName qName, Augmentations augmentations) throws XNIException
   {
      try {
         Name nm = names[level];
         //System.out.println("End element: "+nm+", "+level);
         TypeDefinition tdef = types[level];
         level--;
         ElementEnd end = createElementEnd(nm,tdef,augmentations);
         output.send(end);
      } catch (XMLException ex) {
         throw new XNIException(ex.getMessage(),ex);
      }
   }

   public void endGeneralEntity(String str, Augmentations augmentations) throws XNIException
   {
   }

   public org.apache.xerces.xni.parser.XMLDocumentSource getDocumentSource()
   {
      return null;
   }

   public void ignorableWhitespace(XMLString xMLString, Augmentations augmentations) throws XNIException
   {
   }

   public void processingInstruction(String str, XMLString xMLString, Augmentations augmentations) throws XNIException
   {
      try {
         output.send(constructor.createProcessingInstruction(infoset.createName(str),xMLString.toString()));
      } catch (XMLException ex) {
         throw new XNIException(ex.getMessage(),ex);
      }
   }

   public void setDocumentSource(org.apache.xerces.xni.parser.XMLDocumentSource xMLDocumentSource)
   {
   }

   public void startCDATA(Augmentations augmentations) throws XNIException
   {
   }

   public void startDocument(XMLLocator xMLLocator, String str, NamespaceContext namespaceContext, Augmentations augmentations) throws XNIException
   {
      try {
         this.namespaceContext = namespaceContext;
         String sysid = xMLLocator.getLiteralSystemId();
         output.send(constructor.createDocument(sysid==null ? null : new URI(sysid)));
         level = -1;
      } catch (XMLException ex) {
         throw new XNIException(ex.getMessage(),ex);
      } catch (URISyntaxException ex) {
         throw new XNIException(ex.getMessage(),ex);
      }
   }

   public void startElement(QName qName, XMLAttributes xMLAttributes, Augmentations augmentations) throws XNIException
   {
      try {
         level++;
         if (level==names.length) {
            Name [] newnames = new Name[names.length+INCREMENT];
            System.arraycopy(names,0, newnames,0, names.length);
            names = newnames;
            TypeDefinition [] newtypes = new TypeDefinition[names.length+INCREMENT];
            System.arraycopy(types,0, newtypes,0, types.length);
            types = newtypes;
         }
         Element start = createElement(qName,xMLAttributes,augmentations);
         names[level] = start.getName();
         types[level] = start.getTypeDefinition();
         //System.out.println("End element: "+names[level]+", "+level);
         output.send(start);
      } catch (XMLException ex) {
         throw new XNIException(ex.getMessage(),ex);
      } catch (URISyntaxException ex) {
         throw new XNIException(ex.getMessage(),ex);
      }
   }

   public void startGeneralEntity(String str, XMLResourceIdentifier xMLResourceIdentifier, String str2, Augmentations augmentations) throws XNIException
   {
   }

   public void textDecl(String str, String str1, Augmentations augmentations) throws XNIException
   {
   }

   public void xmlDecl(String str, String str1, String str2, Augmentations augmentations) throws XNIException
   {
   }
   
}
